﻿#pragma once

enum class LogLevel
{
    TRACE_,
    DEBUG_,
    INFO_,
    WARNING_,
    ERROR_,
    FATAL_
};
std::string log_level_string(LogLevel level);
LogLevel parse_log_level(const std::string &level_str);

struct ConsoleConfig
{
    bool enabled = false;
    LogLevel min_level = LogLevel::INFO_;
    bool operator==(const ConsoleConfig &cfg) const
    {
        return enabled == cfg.enabled && min_level == cfg.min_level;
    }

    bool operator!=(const ConsoleConfig &rhs) const
    {
        return !(*this == rhs);
    }
};

struct DatabaseConfig
{
    std::string type;
    std::string host;
    int32_t port = 0;
    std::string username;
    std::string password;
    std::string database;
    std::string table = "logs";
    bool enabled = false;
    LogLevel min_level = LogLevel::INFO_;
    bool operator==(const DatabaseConfig &rhs) const
    {
        return type == rhs.type && host == rhs.host && port == rhs.port && username == rhs.username && password == rhs.password && database == rhs.database && table == rhs.table && enabled == rhs.enabled && min_level == rhs.min_level;
    }

    bool operator!=(const DatabaseConfig &rhs) const
    {
        return !(*this == rhs);
    }
};

struct FileConfig
{
    std::string path_pattern;
    size_t max_size = 100 * 1024 * 1024; // 100MB
    size_t max_files = 10;
    size_t rotation_interval = 86400;
    bool enabled = false;
    LogLevel min_level = LogLevel::INFO_;
    bool operator==(const FileConfig &rhs) const
    {
        return path_pattern == rhs.path_pattern && max_size == rhs.max_size && max_files == rhs.max_files && rotation_interval == rhs.rotation_interval && enabled == rhs.enabled && min_level == rhs.min_level;
    }

    bool operator!=(const FileConfig &rhs) const
    {
        return !(*this == rhs);
    }
};

struct OutputConfig
{
    ConsoleConfig console;
    FileConfig file;
    DatabaseConfig database;

    bool operator==(const OutputConfig &cfg) const
    {
        return console == cfg.console && file == cfg.file && database == cfg.database;
    }

    bool operator!=(const OutputConfig &rhs) const
    {
        return !(*this == rhs);
    }

    friend std::ostream &operator<<(std::ostream &os, const OutputConfig &config)
    {
        os << "OutputConfig:" << std::endl;
        os << "{";

        // 输出控制台配置
        const auto &cc = config.console;
        os << "    console: ";
        os << "    {" << std::endl;
        os << "        enabled: " << (cc.enabled ? "true" : "false") << std::endl;
        os << "        min_level: " << log_level_string(cc.min_level) << std::endl;
        os << "    }" << std::endl;

        // 输出文件配置
        os << "    file: ";
        const auto &fc = config.file;
        os << "    {" << std::endl;
        os << "        enabled: " << (fc.enabled ? "true" : "false") << std::endl;
        os << "        path_pattern: " << fc.path_pattern << std::endl;
        os << "        max_size: " << fc.max_size << std::endl;
        os << "        max_files: " << fc.max_files << std::endl;
        os << "        rotation_interval: " << fc.rotation_interval << std::endl;
        os << "        min_level: " << log_level_string(fc.min_level) << std::endl;
        os << "    }" << std::endl;

        // 输出数据库配置
        os << "    database: ";
        const auto &dc = config.database;
        os << "    {" << std::endl;
        os << "        enabled: " << (dc.enabled ? "true" : "false") << std::endl;
        os << "        type: " << dc.type << std::endl;
        os << "        host: " << dc.host << std::endl;
        os << "        port: " << dc.port << std::endl;
        os << "        username: " << dc.username << std::endl;
        os << "        password: " << dc.password << std::endl;
        os << "        database: " << dc.database << std::endl;
        os << "        table: " << dc.table << std::endl;
        os << "        min_level: " << log_level_string(dc.min_level) << std::endl;
        os << "    }" << std::endl;

        os << "}";

        return os;
    }
};

class LogProcessor
{
public:
    virtual ~LogProcessor() = default;
    virtual void process(const std::string &message, LogLevel level) = 0;
    virtual bool should_process(LogLevel msg_level) const = 0;
    virtual std::unique_ptr<LogProcessor> clone() const = 0;
};

class ConfigManager
{
public:
    ConfigManager(sw::redis::Redis &redis);
    void refresh();
    OutputConfig get_config(const std::string &channel) const;
    std::unordered_set<std::string> get_changed_channels();

private:
    OutputConfig parse_config(const std::unordered_map<std::string, std::string> &fields) const;

    sw::redis::Redis &redis_;
    mutable std::mutex mutex_;
    std::unordered_map<std::string, OutputConfig> configs_;
    std::unordered_set<std::string> pending_changes_;
};

class LogService
{
public:
    LogService(const std::string &redis_host, int32_t redis_port);
    ~LogService();
    void start();
    void stop();

private:
    void subscription_thread();
    void processing_thread();
    void apply_config_changes();
    std::unique_ptr<LogProcessor> create_file_processor(const FileConfig &config, const std::string &channel);
    std::unique_ptr<LogProcessor> create_db_processor(const DatabaseConfig &config, const std::string &channel);

    std::shared_ptr<sw::redis::Redis> redis_;
    std::shared_ptr<ConfigManager> config_manager_;
    std::atomic<bool> running_{false};
    std::thread sub_thread_;
    std::thread processing_thread_;
    std::mutex queue_mutex_;
    std::queue<std::pair<std::string, std::string>> msg_queue_;
    std::mutex processors_mutex_;
    std::unordered_map<std::string, std::vector<std::unique_ptr<LogProcessor>>> processors_;

public:
    std::mutex file_handles_mutex_;
    std::unordered_map<std::string, std::ofstream> file_handles_;
    std::unordered_map<std::string, std::time_t> file_handles_his_;
};

class ConsoleProcessor : public LogProcessor
{
public:
    ConsoleProcessor(OutputConfig config);
    void process(const std::string &msg, LogLevel) override;
    bool should_process(LogLevel msg_level) const override;
    std::unique_ptr<LogProcessor> clone() const override;

private:
    OutputConfig config_;
};

class FileProcessor : public LogProcessor
{
public:
    FileProcessor(LogService *service, FileConfig cfg, std::string ch);
    ~FileProcessor();
    void process(const std::string &msg, LogLevel) override;
    bool should_process(LogLevel msg_level) const override;
    std::unique_ptr<LogProcessor> clone() const override;

private:
    void replace_placeholder(std::string &str, const std::string &placeholder, const std::string &fmt) const;
    std::string generate_filename(const std::chrono::system_clock::time_point &timepoint, int32_t index) const;
    bool should_rotation() const;
    void rotate_files(bool byTime);

    LogService *service_;
    FileConfig config_;
    std::string channel_;
    std::chrono::system_clock::time_point last_rotation_;
};

class MySQLProcessor : public LogProcessor
{
public:
    MySQLProcessor(LogService *service, DatabaseConfig cfg, std::string ch);
    MySQLProcessor(const MySQLProcessor &other);
    ~MySQLProcessor();
    void process(const std::string &msg, LogLevel level) override;
    bool should_process(LogLevel msg_level) const override;
    std::unique_ptr<LogProcessor> clone() const override;
    std::unique_ptr<DBWrapper> createConnection();
    std::vector<std::unique_ptr<DBWrapper>> &get_conn_pool()
    {
        return connections_pool_;
    }
    std::mutex &get_conn_mutex() const
    {
        return connections_mutex_;
    }
    std::vector<std::unique_ptr<DBWrapper>> connections_pool_;

private:
    LogService *service_;
    DatabaseConfig config_;
    std::string channel_;
    mutable std::mutex connections_mutex_;
};